#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <atomic>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <boost/algorithm/string.hpp>
#include <nlohmann/json.hpp>
using namespace std;

class TimeUtil
{
public:
    static string GetTimeStamp()
    {
        struct timeval _time;
        gettimeofday(&_time, nullptr);
        return to_string(_time.tv_sec);
    }
    //获得毫秒时间戳
    static string GetTimeMs()
    {
        struct timeval _time;
        gettimeofday(&_time, nullptr);
        return to_string(_time.tv_sec * 1000 + _time.tv_usec / 1000);
    }
};

const string temp_path = "./temp/";

class PathUtil
{
public:
    static string AddSuffix(const string &file_name, const string &suffix)
    {
        string path_name = temp_path;
        path_name += file_name;
        path_name += suffix;
        return path_name;
    }
    // 编译时需要有的临时文件
    // 构建源文件路径+后缀的完整文件名
    // 1234 -> ./temp/1234.cpp
    static string Src(const string &file_name)
    {
        return AddSuffix(file_name, ".cpp");
    }
    // 构建可执行程序的完整路径+后缀名
    static string Exe(const string &file_name)
    {
        return AddSuffix(file_name, ".exe");
    }
    static string CompilerError(const string &file_name)
    {
        return AddSuffix(file_name, ".compile_error");
    }

    // 运行时需要的临时文件
    static string Stdin(const string &file_name)
    {
        return AddSuffix(file_name, ".stdin");
    }
    static string Stdout(const string &file_name)
    {
        return AddSuffix(file_name, ".stdout");
    }
    // 构建该程序对应的标准错误完整的路径+后缀名
    static string Stderr(const string &file_name)
    {
        return AddSuffix(file_name, ".stderr");
    }
};

class FileUtil
{
public:
    static bool IsFileExists(const string &path_name)
    {
        struct stat st;
        if (stat(path_name.c_str(), &st) == 0)
        {
            //获取属性成功，文件已经存在
            return true;
        }

        return false;
    }
    static string UniqFileName()
    {
        static atomic_uint id(0);
        id++;
        // 毫秒级时间戳+原子性递增唯一值: 来保证唯一性
        string ms = TimeUtil::GetTimeMs();
        string uniq_id = to_string(id);
        return ms + "_" + uniq_id;
    }
    static bool WriteFile(const string &target, const string &content)
    {
        ofstream out(target);
        if (!out.is_open())
        {
            return false;
        }
        out.write(content.c_str(), content.size());
        out.close();
        return true;
    }
    static bool ReadFile(const string &target, string *content, bool keep = false)
    {
        (*content).clear();

        ifstream in(target);
        if (!in.is_open())
        {
            return false;
        }
        string line;
        // getline:不保存行分割符,有些时候需要保留\n,
        // getline内部重载了强制类型转化
        while (getline(in, line))
        {
            (*content) += line;
            (*content) += (keep ? "\n" : "");
        }
        in.close();
        return true;
    }
};

class StringUtil
{
public:
    static void SplitString(const string &str, vector<string> *target, const string &sep)
    {
        //boost split
        boost::split((*target), str, boost::is_any_of(sep), boost::algorithm::token_compress_on);
    }
};

class DataBaseConf
{
    using json = nlohmann::json;
public:
    struct DatabaseConfig 
    {
        string host;
        string username;
        string password;
        string dbName;
    };

    // 使用静态局部变量确保只初始化一次
    static DatabaseConfig& getDatabaseConfig(const string& jsonfilepath)
    {
        static DatabaseConfig config;
        static bool isFirstTime = true;
        if (isFirstTime)
        {
            isFirstTime = false;
            ifstream configFile(jsonfilepath);
            if (!configFile.is_open())
            {
                cout << "Failed to open db_config.json" << endl;
            }
            json jsonData;
            configFile >> jsonData;
            config.host = jsonData["database"]["host"].get<string>();
            config.username = jsonData["database"]["username"].get<string>();
            config.password = jsonData["database"]["password"].get<string>();
            config.dbName = jsonData["database"]["database_name"].get<string>();
            configFile.close();
        }
        return config;
    }
};